<?php

class McCube_Router{

    protected static $_instance = null;
    public    static $_subPath  = '';

    public static function getInstanceOf(){
        if(!self::$_instance)
            self::$_instance = new self();

        return self::$_instance;
    }

     public static function setSubPath($subPath){
        self::$_subPath = $subPath;
    }

    public    $rules        = array();
    public    $requestUri   = '';
    public    $ajaxRequest  = false;
    public    $rulesDefault = array(

        '/' => array(
             'default'=> array('controller'=> 'home', 'action'=> 'index')
        )

        // Controller Rules
        ,'/:controller'            => array(
             'default'=> array('action'=> 'index')
            ,'format' => array('controller'=> 'string_lcFirst')
        )
        ,'/:controller/:action'    => array(
            'format' => array('controller'=> 'string_lcFirst', 'action'=> 'string')
        )
        ,'/:controller/:id'        => array(
             'default'=> array('action'=> 'index')
            ,'format' => array('controller'=> 'string_lcFirst', 'id'=> 'int')
        )
        ,'/:controller/:id/:action'=> array(
            'format' => array('controller'=> 'string_lcFirst', 'id'=> 'int', 'action'=> 'string')
        )
        ,'/:controller/:id/:action/:param1'=> array(
            'format' => array('controller'=> 'string_lcFirst', 'id'=> 'int', 'action'=> 'string', 'param1'=> 'string')
        )

        // Plugin Rules
        ,'/:plugin/'                       => array(
             'default'=> array('controller'=> 'home', 'action'=> 'index')
            ,'format' => array('plugin'=> 'string_ucFirst')
        )
        ,'/:plugin/:controller'            => array(
             'default'=> array('action'=> 'index')
            ,'format' => array('plugin'=> 'string_ucFirst', 'controller'=> 'string_lcFirst')
        )
        ,'/:plugin/:controller/:action'    => array(
            'format' => array('plugin'=> 'string_ucFirst', 'controller'=> 'string_lcFirst', 'action'=> 'string')
        )
        ,'/:plugin/:controller/:id'        => array(
             'default'=> array('action'=> 'view')
            ,'format' => array('plugin'=> 'string_ucFirst', 'controller'=> 'string_lcFirst', 'id'=> 'int')
        )
        ,'/:plugin/:controller/:id/:action'=> array(
            'format' => array('plugin'=> 'string_ucFirst', 'controller'=> 'string_lcFirst', 'id'=> 'int', 'action'=> 'string')
        )
        ,'/:plugin/:id/:action'=> array(
            'default'=> array('controller'=> 'home')
            ,'format' => array('plugin'=> 'string_ucFirst', 'id'=> 'int', 'action'=> 'string')
        )
        ,'/:plugin/:controller/:id/:action/:param1'=> array(
            'format' => array('plugin'=> 'string_ucFirst', 'controller'=> 'string_lcFirst', 'id'=> 'int', 'action'=> 'string', 'param1'=> 'string')
        )
        ,'/:plugin/:id/:action/:param1'=> array(
            'default'=> array('controller'=> 'home')
            ,'format' => array('plugin'=> 'string_ucFirst', 'id'=> 'int', 'action'=> 'string', 'param1'=> 'string')
        )

    );

    /** */
    public function __construct($activateRulesDefault=true){
        if(isset($_SERVER['HTTP_X_REQUESTED_WITH'])
            && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest')
            $this->ajaxRequest = true;

        if($activateRulesDefault)
            $this->addRules($this->rulesDefault);
    }

    public function isAjaxRequest(){
        return $this->ajaxRequest;
    }

    /** */
    public function addRule($rule, $options=array()){
        $this->rules[$rule] = new McCube_Route($rule, $options);
    }

    /** */
    public function addRules($rules){
        foreach($rules as $rule => $options)
            $this->addRule($rule, $options);
    }

    public function getRoute(){
        return $this->currentRule->values;
    }

    /** */
    public function parse($string){
        foreach($this->rules as $rule){
            if($rule->isMatch($string)){
                $this->currentRule = $rule;
                return $this->currentRule->values;
            }
        }
    }
}

class McCube_Route{

    public $rule    = '';
    public $options = array();
    public $formats = array(
         'int'    => "[\d]{1,}"
        ,'string' => "[\w]{1,}"
        ,'string_uc' => "[A-Z]{1,}"
        ,'string_lc' => "[a-z]{1,}"
        ,'string_ucFirst' => "[A-Z]{1}[\w]{1,}"
        ,'string_lcFirst' => "[a-z]{1}[\w]{1,}"
    );

    /** */
    public function __construct($rule, $options=array()){
        $this->rule     = $rule;
        $this->options  = $options;
    }

    /** */
    public function isMatch($uri){
        $rule = preg_replace('@:([a-z1-9]+)@', '(?P<\1>[^/]+)', $this->rule);

        if(!preg_match('@^(?:/|)'.$rule.'(?:/|)$@', $uri, $matches))
            return false;

        $this->values = $matches;
        if(isset($this->options['default']))
            $this->values = array_merge($this->options['default'], $this->values);

        if(!isset($this->options['format']))
            return $this->values;

        if($this->_rangeDatas())
            return $this->values;
    }

    /** */
    protected function _rangeDatas(){
        foreach($this->options['format'] as $key => $format){
            if(!isset($this->values[$key]))
                return false;

            if(!$this->_checkFormat($format, $this->values[$key]))
                return false;
        }

        return true;
    }

    /** */
    protected function _checkFormat($format, $value){
        if(preg_match('@^'.$this->formats[$format].'$@', $value))
            return true;
    }
}